package grinch.edu.probtheory;

import java.util.Random;

/**
 * RandomExtra class is a pseudorandom numbers generator
 * Moscow Aviation Institute
 * Probability theory and mathematical statistics. Part of course work
 * 
 * @author Artem Grinko <gd3776@gmail.com>
 * @version 1.0
 * @since 2012-05-12
 */

public class RandomExtra{
	
	static final int METHOD_NORMAL =  00001;
	static final int METHOD_UNIFORM = 00002;
	static final int METHOD_LAPLASS = 00003;
	private Random rnd = new Random();
	
	
	/**
	 * Pseudorandom number generator
	 * @param method it is an algorithm for generating a sequence of numbers. May be:
	 * <ul>
	 * <li><code>RandomExtra.METHOD_NORMAL</code>
	 * <li><code>RandomExtra.METHOD_UNIFORM</code>
	 * <li><code>RandomExtra.METHOD_LAPLASS</code>
	 * </ul>		  
	 * @param mx	Expected value
	 * @param disp	Dispersion is sigma^2
	 * @return	Return pseudorandom number for current method, mx and disp
	 * @since	1.0
	 */
	public double getRandom(int method,double mx,double disp){
		double sigma = Math.sqrt(disp);
		
		switch (method){
		case METHOD_NORMAL: return gauss(mx,sigma);
		case METHOD_UNIFORM: return uniform(mx,sigma);
		case METHOD_LAPLASS: return laplass(mx,sigma);
		default : return 0;
		}
	}
	/*
	 * The normal(or Gaussian) distribution It uses the Box–Muller transform
	 * http://en.wikipedia.org/wiki/Box-Muller_transform
	 * @param mx	Expected value
	 * @param sigma	Sigma is <code>Math.sqrt(Dispersion)</code>
	 * @return	Pseudorandom number
	 */
	private double gauss(double mx,double sigma){
		double a = 2*rnd.nextFloat() - 1;
		double b = 2*rnd.nextFloat() - 1;
		
		double r = (Math.pow(a, 2) + Math.pow(b, 2));
		for (;r>1;){
			a = 2*rnd.nextFloat() - 1;
			b = 2*rnd.nextFloat() - 1;
			r = (Math.pow(a, 2) + Math.pow(b, 2));			
		}
		
		double sq = Math.sqrt(-2*Math.log(r)/r);
		return mx + sigma * a * sq;
	}
	
	/*
	 * The Laplace distribution
	 * http://en.wikipedia.org/wiki/Laplace_distribution
	 * @param mx	Expected value
	 * @param sigma	Sigma is <code>Math.sqrt(Dispersion)</code>
	 * @return	Pseudorandom number
	 */
	private double laplass(double mx,double sigma){
		
		double a = Math.sqrt(2/Math.pow(sigma, 2));
		double b = mx;
		
		if (rnd.nextFloat()*Math.pow(-1, rnd.nextInt(2))+b<b){
			return (Math.log(rnd.nextFloat())/a+b);
		}else{
			return (-Math.log(-(rnd.nextFloat()-1))/a+b);
		}
		
	}
	
	/*
	 * The discrete uniform distribution
	 * http://en.wikipedia.org/wiki/Uniform_distribution_(discrete)	
	 * @param mx	Expected value. Mean = (a+b)/2
	 * @param sigma	Sigma is <code>Math.sqrt(Dispersion)</code>. 
	 * <code>Sigma = (b-a)/Math.sqrt(12)</code>
	 * @return	Pseudorandom number
	 */
	private double uniform(double mx,double sigma){
		
		double h = (sigma * Math.sqrt(3));
		double a = mx - h;
		double b = mx + h;
		
		return a + (rnd.nextFloat()*(b-a));
	}
	
}
